package src.ORM;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Handler {
    //用于SQLSession的结果集封装，可以封装成对象/Map集合

    /***
     * 该类可将ResultSet结果集对象映射为Class对象。
     * @param set
     * @param classType
     * @return
     * @throws SQLException
     */
    public  <T> T mapperClass(ResultSet set,Class<?> classType) throws SQLException {
        Object result = null;
        //该方法可以将Set集合转换为Class对象
        if(classType == Integer.class || classType == int.class){
            result = set.getInt(1);
        }else if (classType == Float.class || classType == float.class){
            result = set.getFloat(1);
        }else if(classType == Double.class || classType == double.class){
            result = set.getDouble(1);
        }else if(classType == String.class){
            result = set.getString(1);
        }else{
            if(classType == Map.class){
                //表示需要将结果集转换为Map容器
                result = this.toMap(set);
            }else{
                //表示需要转换为Class对象
                result = this.toObject(set,classType);
            }
        }
        return (T)result;
    }

    //将结果集自动转换为Map对象
    private Map<String,Object> toMap(ResultSet set) throws SQLException {
        Map<String,Object> map = new HashMap<>();
        ResultSetMetaData rsm = set.getMetaData();
        for (int i = 1;i<=rsm.getColumnCount();i++){
            //通过Rs结果集中的列名和对应的值来创建K-V集合
            map.put(rsm.getColumnName(i),set.getObject(rsm.getColumnName(i)));
        }
        return map;
    }

    //通过反射的方法来获取cls对象的属性列表，在通过set集合的列名来给类设置属性值。
    private Object toObject(ResultSet set,Class cls) throws SQLException {
        ResultSetMetaData rsm = set.getMetaData();
        Object obj = null;
        try {
            obj = cls.newInstance();
            for(int i = 1;i<=rsm.getColumnCount();i++){
//                ············································
//                方法一  反射直接修改字段
//                //通过反射获取类的字段（属性）信息
//                Field field = obj.getClass().getDeclaredField(rsm.getColumnName(i));
//                //设置私有属性是可修改的
//                field.setAccessible(true);
//                //直接设置属性值
//                field.set(obj,set.getObject(rsm.getColumnName(i)));
//                ·············································
                //通过反射获取类中的set方法
                String methodName = "set"+rsm.getColumnName(i).substring(0,1).toUpperCase()+rsm.getColumnName(i).substring(1);
                Field field = cls.getDeclaredField(rsm.getColumnName(i));//根据属性名称获取单个属性
                //通过属性的类型来调用类中的Setter方法，如setId(Integer.class);
                if (field.getGenericType().toString().equals("int")||field.getGenericType().toString().equals("class java.lang.Integer")){
                    Method method = obj.getClass().getDeclaredMethod(methodName,Integer.class);
                    method.invoke(obj,set.getInt(rsm.getColumnName(i)));
                }else if(field.getGenericType().toString().equals("string")||field.getGenericType().toString().equals("class java.lang.String")){
                    Method method = obj.getClass().getDeclaredMethod(methodName,String.class);
                    method.invoke(obj,set.getString(rsm.getColumnName(i)));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    //解析SQL语句
    public List<Object> parseSQL(String sql){
        List<Object> params = new ArrayList<>();
        StringBuilder sb = new StringBuilder(sql);
        StringBuilder newSQL = new StringBuilder();
        while (true){
            int left = sb.indexOf("#{");
            int right = sb.indexOf("}");
            if(left!=-1 && right!=-1 && left<right){
                //取出在#{左侧的所有
                newSQL.append(sb.substring(0,left));
                //获取参数名 #{name}
                String paramName = sb.substring(left+2,right);
                //将获取的参数名放入List中
                params.add(paramName);
                //用问号填充参数
                newSQL.append("?");
                //从后面的再找
                sb = new StringBuilder(sb.substring(right+1));
            }else{
                //说明本次循环已经找到了,将剩余的字符填充
                newSQL.append(sb.substring(right+1));
                break;
            }
        }
        params.add(newSQL.toString());
        return params;
    }

    //给预处理的SQL语句自动赋值
    public void setParams(PreparedStatement pst,Object obj,List<Object> params) throws Exception {
        Class<?> cls = obj.getClass();
        if(cls == Integer.class || cls == String.class || cls == Float.class || cls == Double.class){
            //说明传入的是一个基本参数，直接赋值
            pst.setObject(1, obj);
        }else if(obj instanceof Map){
            //说明传入的是Map对象
            Map map = (Map) obj;
            for(int i = 0;i<params.size()-1;i++){
                //通过参数名来查询Map集合中的属性
                Object value = map.get(params.get(i));
                pst.setObject(i+1,value);
            }
        }else {
            //传入的是其他对象
            //反射对象获取对象的相应属性
            for (int i = 0;i<params.size()-1;i++){
                //通过反射获取相应字段
                Field field = obj.getClass().getDeclaredField((String) params.get(i));
                //设置字段可访问
                field.setAccessible(true);
                //获取字段对应的值
                Object value = field.get(obj);
                //通过jdbc原生对预处理赋值
                pst.setObject(i+1,value);
            }

        }
    }

    @Test
    public void testSQL(){
       List<Object> list = parseSQL("select * from user where id = #{id} and name = #{name} order by id desc");
       for (Object obj:list){
           System.out.println(obj);
       }
    }
}
